package cgl.narada.service.p2pRelayServer;

import java.net.SocketAddress;
import java.util.Hashtable;

import org.apache.log4j.Logger;

import cgl.narada.protocol.Protocol;
import cgl.narada.transport.up2p.messages.P2PConnectionRequest;
import cgl.narada.transport.up2p.messages.P2PConnectionResponse;
import cgl.narada.transport.up2p.messages.P2PConnectionSetupRequest;
import cgl.narada.transport.up2p.messages.Registration;
import cgl.narada.transport.up2p.messages.RegistrationResponse;

/**
 * 
 * Created on Aug 7, 2006
 * 
 * @author Harshawardhan Gadgil (hgadgil@grids.ucs.indiana.edu)
 */

public class RelayServer implements DataProcessor {

   static Logger log = Logger.getLogger("RelayServer");

   public static final int UDP_PORT = 60055;

   private Hashtable clientTable;
   private UDPConnector udp;

   private Hashtable lookupRequestTable;

   public RelayServer() {
      clientTable = new Hashtable();
      lookupRequestTable = new Hashtable();

      udp = new UDPConnector(this, UDP_PORT);
      udp.start();
   }

   /**
    * @see cgl.hpsearch.NAT.relay.DataProcessor#processData(java.net.SocketAddress,
    *      byte[])
    */
   public synchronized void processData(SocketAddress src, byte[] data) {

      byte header = data[0];
      byte[] payload = new byte[data.length - 1];
      System.arraycopy(data, 1, payload, 0, payload.length);

      if (data[0] == Protocol.REGISTER_PEER) {

         // -----------------------------------------------
         // Protocol.REGISTER_PEER
         // -----------------------------------------------
         Registration reg = Registration.createObjectFromBytes(payload);

         ClientInformation clientInfo = (ClientInformation) clientTable.get(reg
                  .getClientID());
         if (clientInfo == null) {
            clientInfo = new ClientInformation();
            clientInfo.setClientID(reg.getClientID());

            log.info("New PeerUDPRecipient Registration G:{" + src + "}, P:{"
                     + reg.getPrivateAddress() + "}");
         }

         clientInfo.setGlobalAddress(src);
         clientInfo.setPrivateAddress(reg.getPrivateAddress());

         clientTable.put(clientInfo.getClientID(), clientInfo);
         // Now send registration resp
         RegistrationResponse resp = new RegistrationResponse(src);

         byte[] response = generateResponse(Protocol.REGISTER_PEER_RESPONSE,
                  resp.getBytes());

         try {
            udp.send(src, response);
         } catch (Exception e) {
            log.error("", e);
         }
      }

      else if (data[0] == Protocol.P2P_CONN_REQUEST) {
         // -----------------------------------------------
         // Protocol.P2P_CONN_REQUEST
         // -----------------------------------------------

         P2PConnectionRequest request = P2PConnectionRequest
                  .createObjectFromBytes(payload);
         String peerId = request.getDestinationPeerID();

         String magic = request.getMagic();

         log.info("Processing MAGIC <" + magic + ">");

         ClientInformation clientInfo = (ClientInformation) clientTable
                  .get(peerId);
         if (clientInfo != null) {
            P2PConnectionSetupRequest setupReq = new P2PConnectionSetupRequest(
                     src, magic);

            // Forward the request to requested peer
            byte[] response = generateResponse(Protocol.P2P_CONN_REQUEST,
                     setupReq.getBytes());

            try {
               log.info("Forwarding P2P_CONN_REQUEST to {"
                        + clientInfo.getGlobalAddress() + "}");
               udp.send(clientInfo.getGlobalAddress(), response);
            } catch (Exception e) {
               log.error("", e);
            }

            // Also reply back the public / private address of destination peer

            P2PConnectionResponse resp = new P2PConnectionResponse(clientInfo
                     .getGlobalAddress(), clientInfo.getPrivateAddress(), magic);

            byte[] connResp = generateResponse(Protocol.P2P_CONN_RESPONSE, resp
                     .getBytes());

            try {
               log.info("Sending P2P_CONN_RESPONSE to {" + src + "}");

               udp.send(src, connResp);
            } catch (Exception e) {
               log.error("", e);
            }

         } // else DISCARD for now...

      }
   }

   private byte[] generateResponse(byte type, byte[] payload) {
      byte[] result = new byte[payload.length + 1];
      result[0] = type;
      System.arraycopy(payload, 0, result, 1, payload.length);
      return result;
   }

   /**
    * @param args
    */
   public static void main(String[] args) {
      RelayServer server = new RelayServer();
   }
}